package com.ruihu.admin.module.business.task.service;

import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.plugin.table.LoopColumnTableRenderPolicy;
import com.ruihu.admin.common.anno.MethodLog;
import com.ruihu.admin.common.constant.GlobalConst;
import com.ruihu.admin.common.constant.JudgeEnum;
import com.ruihu.admin.common.constant.OKEnum;
import com.ruihu.admin.common.domain.PageResultDTO;
import com.ruihu.admin.common.domain.ResponseDTO;
import com.ruihu.admin.common.exception.BusinessException;
import com.ruihu.admin.common.word.RHLoopRowColumnTableRenderPolicy;
import com.ruihu.admin.constant.ElectricConfigTypeEnum;
import com.ruihu.admin.constant.SystemStatusEnum;
import com.ruihu.admin.module.business.electric.config.dao.ElectricConfigDao;
import com.ruihu.admin.module.business.electric.config.domain.dto.ElectricConfigDTO;
import com.ruihu.admin.module.business.electric.config.domain.vo.ElectricTypeVO;
import com.ruihu.admin.module.business.electric.config.service.CellTypeConfigService;
import com.ruihu.admin.module.business.electric.config.service.ElectricConfigService;
import com.ruihu.admin.module.business.electric.config.service.FactoryConfigService;
import com.ruihu.admin.module.business.task.constant.TaskResponseCodeConst;
import com.ruihu.admin.module.business.task.constant.TaskStatusEnum;
import com.ruihu.admin.module.business.task.constant.TaskStepConstant;
import com.ruihu.admin.module.business.task.converter.SubTaskList;
import com.ruihu.admin.module.business.task.converter.TaskNodeInstList;
import com.ruihu.admin.module.business.task.dao.SubTaskDao;
import com.ruihu.admin.module.business.task.dao.TaskDao;
import com.ruihu.admin.module.business.task.dao.TaskNodeInstDao;
import com.ruihu.admin.module.business.task.domain.dto.*;
import com.ruihu.admin.module.business.task.domain.entity.SubTaskEntity;
import com.ruihu.admin.module.business.task.domain.entity.TaskEntity;
import com.ruihu.admin.module.business.task.domain.entity.TaskNodeInstEntity;
import com.ruihu.admin.module.business.task.domain.vo.*;
import com.ruihu.admin.module.business.task.manager.SubTaskManager;
import com.ruihu.admin.module.business.tasknode.converter.TaskNodeFileList;
import com.ruihu.admin.module.business.tasknode.dao.*;
import com.ruihu.admin.module.business.tasknode.domain.dto.*;
import com.ruihu.admin.module.business.tasknode.domain.entity.DefectTypeEntity;
import com.ruihu.admin.module.business.tasknode.domain.vo.DefectTypeVO;
import com.ruihu.admin.module.business.tasknode.domain.vo.NodeInstProcessVO;
import com.ruihu.admin.module.business.tasknode.domain.vo.SubTaskNodeInstVO;
import com.ruihu.admin.module.business.tasknode.domain.vo.TaskNodeFileVO;
import com.ruihu.admin.module.support.file.constant.FileModuleTypeEnum;
import com.ruihu.admin.module.support.file.constant.FileServiceTypeEnum;
import com.ruihu.admin.module.support.file.service.FileService;
import com.ruihu.admin.module.system.department.DepartmentService;
import com.ruihu.admin.module.system.department.domain.vo.DepartmentVO;
import com.ruihu.admin.module.system.user.UserService;
import com.ruihu.admin.module.system.user.domain.bo.UserBO;
import com.ruihu.admin.util.*;
import com.ruihu.admin.util.word.RHWordExportUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.imageio.stream.FileImageInputStream;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

/**
 * [ 任务子表 ]
 *
 * @author Tony
 * @version 1.0
 * @company 瑞虎（南京）信息技术有限公司
 * @copyright (c)  瑞虎（南京）信息技术有限公司Inc. All rights reserved.
 * @date 2022-08-29 17:13:25
 * @since JDK1.8
 */
@Service
@Slf4j
public class SubTaskService {

    @Autowired
    private SubTaskDao subTaskDao;

    @Autowired
    private TaskNodeInstDao taskNodeInstDao;

    @Autowired
    private UserService userService;

    @Autowired
    private DepartmentService departmentService;

    @Autowired
    private SubTaskNodeInstDao subTaskNodeInstDao;

    @Autowired
    private TaskDao taskDao;

    @Autowired
    private FileService fileService;

    @Autowired
    private TaskNodeFileDao taskNodeFileDao;

    @Autowired
    private SubTaskManager subTaskManager;

    @Autowired
    private ElectricConfigDao electricConfigDao;

    @Autowired
    private DefectTypeDao defectTypeDao;

    @Autowired
    private ElectricConfigService electricConfigService;

    @Autowired
    private CellTypeConfigService cellTypeConfigService;

    @Autowired
    private FactoryConfigService factoryConfigService;

    @Autowired
    private NodeInstProcessRelationDao nodeInstProcessRelationDao;

    @Autowired
    private NodeInstProcessDefectDao nodeInstProcessDefectDao;

    @Value("${file-upload-service.path}")
    private String fileBasePath;

    private static final int IMG_WIDTH = 200;
    private static final int IMG_HEIGHT = 180;

    /**
     * 根据id查询
     */
    public SubTaskEntity getById(Long id) {
        return subTaskDao.selectById(id);
    }

    /**
     * 根据id查询
     */
    @MethodLog
    public ResponseDTO<SubTaskVO> queryById(Long id) {
        SubTaskEntity subTaskEntity = this.subTaskDao.selectById(id);
        if (Objects.isNull(subTaskEntity))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_NOT_FOUND);
        if (JudgeEnum.YES.getValue().toString().equals(subTaskEntity.getIsDelete())) {
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_NOT_FOUND);
        }
        SubTaskVO subTaskVO = SubTaskVO.convertToSubTaskVO(subTaskEntity);
        if (Objects.nonNull(subTaskEntity.getCreateBy())) {
            UserBO createUser = this.userService.getById(subTaskEntity.getCreateBy());
            if (Objects.nonNull(createUser)) {
                subTaskVO.setCreateByName(createUser.getActualName());

                ResponseDTO<DepartmentVO> departmentResp = this.departmentService.getDepartmentById(createUser.getDepartmentId());
                if (departmentResp.isSuccess())
                    subTaskVO.setDepartmentName(departmentResp.getData().getDepartmentName());
            }
        }
        if (Objects.nonNull(subTaskEntity.getUpdateBy())) {
            UserBO updateUser = this.userService.getById(subTaskEntity.getUpdateBy());
            if (Objects.nonNull(updateUser)) {
                subTaskVO.setUpdateByName(updateUser.getActualName());
            }
        }
        if (StrUtil.isNotEmpty(subTaskEntity.getElectricCoreType())) {
            ElectricTypeVO electricTypeVO = this.electricConfigDao.queryElectricTypeByCode(ElectricConfigTypeEnum.ELECTRIC_CORE_TYPE_CONFIG.getCode(), subTaskEntity.getElectricCoreType());
            if (Objects.nonNull(electricTypeVO))
                subTaskVO.setElectricCoreTypeName(electricTypeVO.getType());
        }
        if (Objects.nonNull(subTaskEntity.getStatus())) {
            TaskStatusEnum taskStatusEnum = BaseEnumUtil.getEnumByValue(subTaskEntity.getStatus(), TaskStatusEnum.class);
            subTaskVO.setStatusText(taskStatusEnum.getDesc());
        }

        return ResponseDTO.succData(subTaskVO);
    }

    /**
     * 分页查询
     *
     * @author Tony
     * @date 2022-08-29 17:13:25
     */
    @MethodLog
    public ResponseDTO<PageResultDTO<SubTaskVO>> queryByPage(SubTaskQueryDTO queryDTO) {
        Page page = PageUtil.convert2QueryPage(queryDTO);
        List<SubTaskDTO> subTaskDTOList = subTaskDao.queryByPage(page, queryDTO);
        List<SubTaskVO> subTaskVOList = SubTaskList.convertToSubTaskVOList(subTaskDTOList);
        page.setRecords(subTaskVOList);
        PageResultDTO<SubTaskVO> pageResultDTO = PageUtil.convert2PageResult(page);
        return ResponseDTO.succData(pageResultDTO);
    }

    /**
     * 分页查询待子任务
     *
     * @author Tony
     * @date 2022-08-29 17:13:25
     */
    @MethodLog
    public ResponseDTO<PageResultDTO<SubTaskVO>> querySubTaskByPage(SubTaskQueryDTO queryDTO) {
        // 判断主任务状态是否满足
        TaskEntity taskEntity = this.taskDao.selectById(queryDTO.getMainTaskId());
        if (Objects.isNull(taskEntity)) {
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_NOT_FOUND);
        }

        Page page = PageUtil.convert2QueryPage(queryDTO);
        if (!RequestTokenUtil.getRequestUser().getUserBO().getIsSuperman())
            queryDTO.setAssigneeId(RequestTokenUtil.getRequestUser().getRequestUserId());
        List<SubTaskDTO> subTaskDTOList = subTaskDao.queryByPage(page, queryDTO);
        subTaskDTOList.stream().forEach(e -> {
            if (Objects.nonNull(e.getCreateBy())) {
                UserBO createUser = this.userService.getById(e.getCreateBy());
                if (Objects.nonNull(createUser)) {
                    e.setCreateByName(createUser.getActualName());
                }
            }
            if (Objects.nonNull(e.getUpdateBy())) {
                UserBO updateUser = this.userService.getById(e.getUpdateBy());
                if (Objects.nonNull(updateUser)) {
                    e.setUpdateByName(updateUser.getActualName());
                }
            }
        });
        List<SubTaskVO> subTaskVOList = SubTaskList.convertToSubTaskVOList(subTaskDTOList);
        subTaskVOList.forEach(subTaskVO -> {
            TaskStatusEnum taskStatusEnum = BaseEnumUtil.getEnumByValue(subTaskVO.getStatus(), TaskStatusEnum.class);
            if (null != taskStatusEnum) {
                subTaskVO.setStatusText(taskStatusEnum.getDesc());
            }
        });
        page.setRecords(subTaskVOList);
        PageResultDTO<SubTaskVO> pageResultDTO = PageUtil.convert2PageResult(page);
        return ResponseDTO.succData(pageResultDTO);
    }

    /**
     * 分页查询待处理和正在受理的任务
     *
     * @author Tony
     * @date 2022-08-29 17:13:25
     */
    @MethodLog
    public ResponseDTO<PageResultDTO<SubTaskVO>> querySelfAndEmptySubTask(SubTaskQueryDTO queryDTO) {
        // 判断主任务状态是否满足
        TaskEntity taskEntity = this.taskDao.selectById(queryDTO.getMainTaskId());
        if (Objects.isNull(taskEntity)) {
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_NOT_FOUND);
        }
        TaskStatusEnum mainTaskStatusEnum = BaseEnumUtil.getEnumByValue(taskEntity.getStatus(), TaskStatusEnum.class);
        if (!TaskStatusEnum.RUNNING.equals(mainTaskStatusEnum))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_STATUS_NOT_MEET_REQUIRMENT);

        Page page = PageUtil.convert2QueryPage(queryDTO);
        if (!RequestTokenUtil.getRequestUser().getUserBO().getIsSuperman())
            queryDTO.setAssigneeId(RequestTokenUtil.getRequestUser().getRequestUserId());
        List<SubTaskDTO> subTaskDTOList = subTaskDao.querySelfAndEmptySubTask(page, queryDTO);
        subTaskDTOList.stream().forEach(e -> {
            if (Objects.nonNull(e.getCreateBy())) {
                UserBO createUser = this.userService.getById(e.getCreateBy());
                if (Objects.nonNull(createUser)) {
                    e.setCreateByName(createUser.getActualName());
                }
            }
            if (Objects.nonNull(e.getUpdateBy())) {
                UserBO updateUser = this.userService.getById(e.getUpdateBy());
                if (Objects.nonNull(updateUser)) {
                    e.setUpdateByName(updateUser.getActualName());
                }
            }
            // 查询电芯型号
            if (StrUtil.isNotEmpty(e.getElectricCoreType())) {
                ResponseDTO<ElectricConfigDTO> resp = this.electricConfigService.getElectricConfigByCode(e.getElectricCoreType());
                if (Objects.nonNull(resp.getData())) {
                    e.setElectricCoreTypeName(resp.getData().getType());
                }
            }
        });
        List<SubTaskVO> subTaskVOList = SubTaskList.convertToSubTaskVOList(subTaskDTOList);
        subTaskVOList.forEach(subTaskVO -> {
            TaskStatusEnum taskStatusEnum = BaseEnumUtil.getEnumByValue(subTaskVO.getStatus(), TaskStatusEnum.class);
            if (null != taskStatusEnum) {
                subTaskVO.setStatusText(taskStatusEnum.getDesc());
            }
        });
        page.setRecords(subTaskVOList);
        PageResultDTO<SubTaskVO> pageResultDTO = PageUtil.convert2PageResult(page);
        return ResponseDTO.succData(pageResultDTO);
    }

    /**
     * 添加
     *
     * @author Tony
     * @date 2022-08-29 17:13:25
     */
    @MethodLog
    public ResponseDTO<String> add(SubTaskAddDTO addDTO) {
        SubTaskEntity entity = BeanUtil.copy(addDTO, SubTaskEntity.class);
        subTaskDao.insert(entity);
        return ResponseDTO.succ();
    }

    /**
     * 编辑
     *
     * @author Tony
     * @date 2022-08-29 17:13:25
     */
    @MethodLog
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> update(SubTaskUpdateDTO updateDTO) {
        SubTaskEntity entity = BeanUtil.copy(updateDTO, SubTaskEntity.class);
        subTaskDao.updateByIdIgnoreEmptyValue(entity);
        return ResponseDTO.succ();
    }

    /**
     * 删除
     *
     * @author Tony
     * @date 2022-08-29 17:13:25
     */
    @MethodLog
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> deleteByIds(List<Long> idList) {
        subTaskDao.deleteByIdList(idList);
        return ResponseDTO.succ();
    }

    /**
     * 执行任务
     *
     * @param taskId
     * @param subTaskId
     * @return
     */
    @MethodLog
    public ResponseDTO<List<TaskNodeInstVO>> executeSubTask(Long taskId, Long subTaskId) {
        if (Objects.isNull(taskId) || Objects.isNull(subTaskId))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_ID_NULL);

        SubTaskEntity subTaskEntity = this.subTaskDao.selectById(subTaskId);
        if (Objects.isNull(subTaskEntity) || !taskId.equals(subTaskEntity.getMainTaskId()))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_NOT_FOUND);

        // 判断主任务状态是否满足
        TaskEntity taskEntity = this.taskDao.selectById(taskId);
        if (Objects.isNull(taskEntity)) {
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_NOT_FOUND);
        }
        TaskStatusEnum taskStatusEnum = BaseEnumUtil.getEnumByValue(taskEntity.getStatus(), TaskStatusEnum.class);
        if (!TaskStatusEnum.RUNNING.equals(taskStatusEnum))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_STATUS_NOT_MEET_REQUIRMENT);

        // 判断任务是否已经被自己抢做
        TaskStatusEnum subTaskStatusEnum = BaseEnumUtil.getEnumByValue(subTaskEntity.getStatus(), TaskStatusEnum.class);
        UserBO currentUser = RequestTokenUtil.getRequestUser().getUserBO();
        if (!TaskStatusEnum.CREATED.equals(subTaskStatusEnum) && !currentUser.getId().equals(subTaskEntity.getAssigneeId())) {
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_ALREADY_CLAIM_BY_OTHER);
        } else if (!TaskStatusEnum.CREATED.equals(subTaskStatusEnum) && currentUser.getId().equals(subTaskEntity.getAssigneeId())) {
            // 任务被当前用户受理直接返回节点列表
            return ResponseDTO.succData(this.queryTaskNodeInstListByTaskId(taskId, subTaskId));
        } else if (TaskStatusEnum.CREATED.equals(subTaskStatusEnum)) {
            // 抢做当前未执行的任务
            TaskNodeInstQueryDTO taskNodeInstQueryDTO = new TaskNodeInstQueryDTO();
            taskNodeInstQueryDTO.setMainTaskId(taskId);
            List<TaskNodeInstEntity> taskNodeInstEntityList = this.taskNodeInstDao.queryList(taskNodeInstQueryDTO);
            if (taskNodeInstEntityList.size() > 0)  // 设置第一个节点为当前节点
                subTaskEntity.setCurrentTaskNodeInstId(taskNodeInstEntityList.get(0).getId());
            subTaskEntity.setStatus(TaskStatusEnum.RUNNING.getValue());
            subTaskEntity.setUpdateBy(currentUser.getId());
            subTaskEntity.setUpdateTime(new Date());
            subTaskEntity.setAssigneeId(currentUser.getId());
            subTaskEntity.setAssigneeName(currentUser.getActualName());
            int affectCount = this.subTaskDao.claimSubTask(subTaskEntity);
            if (affectCount <= 0) { // 抢单任务失败
                return ResponseDTO.wrap(TaskResponseCodeConst.TASK_ALREADY_CLAIM_BY_OTHER);
            } else {
                return ResponseDTO.succData(this.queryTaskNodeInstListByTaskId(taskId, subTaskId));
            }
        }
        return ResponseDTO.wrap(TaskResponseCodeConst.TASK_CLAIM_EXCEPTION);
    }

    /**
     * 按顺序查找任务节点实例
     *
     * @param taskId
     * @param subTaskId
     * @return
     */
    @MethodLog
    private List<TaskNodeInstVO> queryTaskNodeInstListByTaskId(Long taskId, Long subTaskId) {
        TaskNodeInstQueryDTO queryDTO = new TaskNodeInstQueryDTO();
        queryDTO.setMainTaskId(taskId);
        queryDTO.setSubTaskId(subTaskId);
        List<TaskNodeInstDTO> taskNodeInstDTOList = this.taskNodeInstDao.queryListOfSubTask(queryDTO);
        List<TaskNodeInstVO> taskNodeInstVOList = TaskNodeInstList.convertDTOToTaskNodeInstVOList(taskNodeInstDTOList);
        return taskNodeInstVOList;
    }

    /**
     * 根据子任务id查看报表
     *
     * @param subTaskId
     * @return
     */
    @MethodLog
    public ResponseDTO<SubTaskAllVO> querySubTaskInstById(Long subTaskId) {
        // 查询子任务信息
        SubTaskEntity subTaskEntity = this.subTaskDao.selectById(subTaskId);
        if (Objects.isNull(subTaskEntity))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_NOT_FOUND);
        SubTaskVO subTaskVO = SubTaskVO.convertToSubTaskVO(subTaskEntity);
        if (Objects.nonNull(subTaskEntity.getCreateBy())) {
            UserBO createUser = this.userService.getById(subTaskEntity.getCreateBy());
            if (Objects.nonNull(createUser)) {
                subTaskVO.setCreateByName(createUser.getActualName());

                ResponseDTO<DepartmentVO> departmentResp = this.departmentService.getDepartmentById(createUser.getDepartmentId());
                if (departmentResp.isSuccess())
                    subTaskVO.setDepartmentName(departmentResp.getData().getDepartmentName());
            }
        }
        //查询主任务信息
        TaskVO taskVO = TaskVO.convertToTaskVO(taskDao.selectById(subTaskEntity.getMainTaskId()));
        SubTaskAllVO SubTaskAllVO = new SubTaskAllVO();
        //查询子任务实例
        SubTaskNodeInstQueryDTO subTaskNodeInstQueryDTO = new SubTaskNodeInstQueryDTO();
        subTaskNodeInstQueryDTO.setSubTaskId(subTaskId);
        List<SubTaskNodeInstDTO> subTaskNodeInstDTOList = this.subTaskNodeInstDao.querySubTaskNodeInstList(subTaskNodeInstQueryDTO);
        TaskNodeFileQueryDTO fileQueryDTO = new TaskNodeFileQueryDTO();
        fileQueryDTO.setSubTaskId(subTaskId);
        List<TaskNodeFileVO> taskNodeFileVos = TaskNodeFileList.convertDTOToTaskNodeFileVOList(this.taskNodeFileDao.queryList(fileQueryDTO));
        if (CollectionUtils.isNotEmpty(taskNodeFileVos)) {
            taskNodeFileVos.forEach(taskNodeFile -> {
                ResponseDTO<String> getFileUrlResp = this.fileService.getFileUrl(taskNodeFile.getFilePath(), FileServiceTypeEnum.LOCAL);
                if (getFileUrlResp.isSuccess()) {
                    taskNodeFile.setUrl(getFileUrlResp.getData());
                }
            });
        }
        Map<Long, List<TaskNodeFileVO>> fileMap = taskNodeFileVos.stream().filter(e -> e.getProcessRelationId() != null)
                .collect(Collectors.groupingBy(TaskNodeFileVO::getProcessRelationId));
        // 查询所有的缺陷列表
        NodeInstProcessDefectQueryDTO processDefectQueryDTO = new NodeInstProcessDefectQueryDTO();
        processDefectQueryDTO.setSubTaskId(subTaskId);
        List< NodeInstProcessDefectDTO> nodeInstProcessDefectList = this.nodeInstProcessDefectDao.queryByList(processDefectQueryDTO);

        //实例Id集合 根据实例id查询表t_node_inst_process_relation对应的属性
        List<SubTaskNodeInstExtVO> subTaskNodeInstExtVOS = new ArrayList<>();
        /**
         * 每个环节的节点元素都有图片配置
         * 循环取出照片数据，去对应任务环节ID
         */
        if (CollectionUtils.isNotEmpty(subTaskNodeInstDTOList)) {
            List<Long> subTaskNodeInstIds = subTaskNodeInstDTOList.stream().map(p -> p.getId()).collect(Collectors.toList());
            List<NodeInstProcessVO> nodeInstProcessVOList = nodeInstProcessRelationDao.queryNodeInstProcessList(subTaskNodeInstIds);
            Map<Long, List<NodeInstProcessVO>> nodeInstProcessVOMap = nodeInstProcessVOList.stream().filter(e -> e.getTaskNodeInstId() != null)
                    .collect(Collectors.groupingBy(NodeInstProcessVO::getTaskNodeInstId));
            subTaskNodeInstDTOList.stream().forEach(subTaskNodeInstDTO -> {
                Long taskNodeInstId = subTaskNodeInstDTO.getTaskNodeInstId();
                SubTaskNodeInstVO subTaskNodeInstVO = SubTaskNodeInstVO.convertToSubTaskNodeInstVO(subTaskNodeInstDTO);
                SubTaskNodeInstExtVO subTaskNodeInstExtVO = new SubTaskNodeInstExtVO();
                subTaskNodeInstExtVO.setTaskNodeName(subTaskNodeInstVO.getTaskNodeName());
                subTaskNodeInstExtVO.setSortNo(subTaskNodeInstVO.getSortNo());
                subTaskNodeInstExtVO.setCellTypeCode(subTaskNodeInstVO.getCellTypeCode());
                subTaskNodeInstExtVO.setElectricCoreCode(subTaskVO.getElectricCoreCode());
                subTaskNodeInstExtVO.setElectricCoreType(subTaskVO.getElectricCoreType());
                subTaskNodeInstExtVO.setFactoryCode(subTaskNodeInstDTO.getFactoryCode());
                if(StringUtils.isNotEmpty(subTaskVO.getElectricCoreType())){
                    ResponseDTO<ElectricConfigDTO> resp = this.electricConfigService.getElectricConfigByCode(subTaskVO.getElectricCoreType());
                    if (Objects.nonNull(resp.getData())) {
                        subTaskVO.setElectricCoreTypeName(resp.getData().getType());
                        subTaskNodeInstExtVO.setElectricCoreTypeName(resp.getData().getType());
                    }
                }
                if(StringUtils.isNotEmpty(subTaskNodeInstDTO.getFactoryCode())){
                    ResponseDTO<ElectricConfigDTO> factoryResponse = this.factoryConfigService.getFactoryConfigByCode(subTaskNodeInstDTO.getFactoryCode());
                    if (factoryResponse.isSuccess()) {
                        subTaskNodeInstExtVO.setFactoryName(factoryResponse.getData().getType());
                    }
                }
                // 处理第一步的缺陷列表
                List<NodeInstProcessDefectDTO> defectTypeListForStep1 = nodeInstProcessDefectList.stream()
                        .filter(e -> Objects.isNull(e.getProcessRelationId()))
                        .collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(defectTypeListForStep1)) {
                    String defectNames = defectTypeListForStep1.stream()
                            .map(NodeInstProcessDefectDTO::getDefectName)
                            .collect(Collectors.joining(StrUtil.COMMA));
                    subTaskNodeInstExtVO.setDefectTypeNames(defectNames);
                }
                if (MapUtils.isNotEmpty(nodeInstProcessVOMap) && nodeInstProcessVOMap.containsKey(taskNodeInstId)) {
                    List<NodeInstProcessVO> nodeInstProcessVOS = nodeInstProcessVOMap.get(taskNodeInstId);
                    if (CollectionUtils.isNotEmpty(nodeInstProcessVOS)) {
                        nodeInstProcessVOS.forEach(nodeInstProcessVO -> {
                            if (MapUtils.isNotEmpty(fileMap) && fileMap.containsKey(nodeInstProcessVO.getId())) {
                                nodeInstProcessVO.setTaskNodeFileList(fileMap.get(nodeInstProcessVO.getId()));
                            }
                            List<DefectTypeVO> defectTypeVOList = nodeInstProcessDefectList.stream()
                                    .filter(e -> Objects.nonNull(e.getProcessRelationId())
                                            && nodeInstProcessVO.getId().equals(e.getProcessRelationId()))
                                    .map(e -> {
                                        DefectTypeVO defectTypeVO = new DefectTypeVO();
                                        defectTypeVO.setDefectTypeId(e.getDefectTypeId());
                                        defectTypeVO.setDefectName(e.getDefectName());
                                        return defectTypeVO;
                                    })
                                    .collect(Collectors.toList());
                            nodeInstProcessVO.setDefectTypeList(defectTypeVOList);
                        });
                        subTaskNodeInstExtVO.setNodeInstProcessVOS(nodeInstProcessVOS);
                    }
                }
                subTaskNodeInstExtVOS.add(subTaskNodeInstExtVO);
            });
        }
        TaskStatusEnum taskStatusEnum = BaseEnumUtil.getEnumByValue(subTaskVO.getStatus(), TaskStatusEnum.class);
        subTaskVO.setStatusText(taskStatusEnum.getDesc());
        if (Objects.nonNull(taskVO)) {
            SubTaskAllVO.setMainTaskVo(taskVO);
        }
        SubTaskAllVO.setSubTaskVo(subTaskVO);
        SubTaskAllVO.setSubTaskNodeInstExtVOS(subTaskNodeInstExtVOS);
        return ResponseDTO.succData(SubTaskAllVO);
    }

    /**
     * 重置子任务
     *
     * @param subTaskId
     * @return
     */
    @MethodLog
    public ResponseDTO<String> resetSubTask(Long subTaskId) {
        if (null == subTaskId)
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_ID_NULL);

        SubTaskEntity subTaskEntity = this.subTaskDao.selectById(subTaskId);
        if (Objects.isNull(subTaskEntity))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_NOT_FOUND);

        TaskStatusEnum subTaskStatus = BaseEnumUtil.getEnumByValue(subTaskEntity.getStatus(), TaskStatusEnum.class);
        if (subTaskStatus.equals(TaskStatusEnum.DISCARD) || subTaskStatus.equals(TaskStatusEnum.ARCHIVE) ||
                subTaskStatus.equals(TaskStatusEnum.COMPLETE))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_STATUS_NOT_MEET_REQUIRMENT);

        UserBO currentUser = RequestTokenUtil.getRequestUser().getUserBO();
        if (!TaskStatusEnum.CREATED.equals(subTaskStatus) && !currentUser.getId().equals(subTaskEntity.getAssigneeId()))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_ALREADY_CLAIM_BY_OTHER);

        this.subTaskManager.resetSubTask(subTaskEntity);
        return ResponseDTO.succ();
    }

    @MethodLog
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<String> cancelSubTaskArchive(Long subTaskId) {
        if (Objects.isNull(subTaskId))
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_ID_NULL);
        SubTaskEntity subTaskEntity = this.subTaskDao.selectById(subTaskId);
        TaskStatusEnum taskStatusEnum = BaseEnumUtil.getEnumByValue(subTaskEntity.getStatus(), TaskStatusEnum.class);
        if (!TaskStatusEnum.COMPLETE.equals(taskStatusEnum)) {
            return ResponseDTO.wrap(TaskResponseCodeConst.TASK_STATUS_NOT_MEET_REQUIRMENT);
        }

        subTaskEntity.setStatus(TaskStatusEnum.RUNNING.getValue());
        subTaskEntity.setCompletionTime(null);
        subTaskEntity.setUpdateTime(new Date());
        subTaskEntity.setUpdateBy(RequestTokenUtil.getRequestUser().getRequestUserId());
        this.subTaskDao.updateById(subTaskEntity);

        // 如果主任务状态为完成，则更新为运行中
        TaskEntity taskEntity = this.taskDao.selectById(subTaskEntity.getMainTaskId());
        taskStatusEnum = BaseEnumUtil.getEnumByValue(taskEntity.getStatus(), TaskStatusEnum.class);
        if (TaskStatusEnum.COMPLETE.equals(taskStatusEnum)) {
            taskEntity.setStatus(TaskStatusEnum.RUNNING.getValue());
            taskEntity.setCompletionTime(null);
            taskEntity.setUpdateTime(new Date());
            taskEntity.setUpdateBy(RequestTokenUtil.getRequestUser().getRequestUserId());
            this.taskDao.updateById(taskEntity);
        }

        return ResponseDTO.succ();
    }

    /**
     * 导出电芯拆解记录
     *
     * @param subTaskId
     * @return
     */
    public XWPFDocument exportSubTask(Long subTaskId) throws Exception {
        ResponseDTO<SubTaskAllVO> SubTaskAllVOResponseDTO = this.querySubTaskInstById(subTaskId);
        if (SubTaskAllVOResponseDTO.isSuccess()) {
            return this.export(SubTaskAllVOResponseDTO.getData());
        }
        return null;
    }

    public XWPFTemplate exportSubTaskByTemplate(Long subTaskId) throws Exception{
        ResponseDTO<SubTaskAllVO> SubTaskAllVOResponseDTO = this.querySubTaskInstById(subTaskId);
        if (SubTaskAllVOResponseDTO.isSuccess()) {
            return this.exportByTemplate(SubTaskAllVOResponseDTO.getData());
        }
        return null;
    }

    public XWPFDocument export(SubTaskAllVO subTaskAllVO) throws Exception{
        Map<String, Object> exportMap = buildSubTaskAllData(subTaskAllVO);
        String templateFile = this.fileBasePath + FileModuleTypeEnum.EXPORT_TEMPLATE.getPath() + FileUtil.FILE_SEPARATOR + "Template.docx";
        TemplateExportParams templateExportParams = new TemplateExportParams(templateFile);
        templateExportParams.setColForEach(true);
        XWPFDocument word = RHWordExportUtil.exportWord07(templateExportParams.getTemplateUrl(),exportMap);
        return word;
    }

    @NotNull
    private Map<String, Object> buildSubTaskAllData(SubTaskAllVO subTaskAllVO) {
        SubTaskVO subTaskVO = subTaskAllVO.getSubTaskVo();
        if (Objects.isNull(subTaskVO))
            throw new BusinessException(TaskResponseCodeConst.TASK_NOT_FOUND);

        List<SubTaskNodeInstExtVO> subTaskNodeInstExtVOS = subTaskAllVO.getSubTaskNodeInstExtVOS();
        Map<String, Object> exportMap = new HashMap<>();
        exportMap.put("date", DateUtil.format(subTaskVO.getCreateTime(), DatePattern.NORM_DATE_FORMAT));
        exportMap.put("taskInfo", subTaskVO);
        if (CollectionUtils.isNotEmpty(subTaskNodeInstExtVOS)) {
            subTaskNodeInstExtVOS.forEach(subTaskNodeInstExtVO -> {
                // 单独处理电芯信息
                if (Objects.equals(subTaskNodeInstExtVO.getSortNo(), TaskStepConstant.STEP_ONE)) {
                    //电芯信息
                    ElectricCoreExportDTO exportDTO = new ElectricCoreExportDTO();
                    exportDTO.setElectricCoreType(subTaskNodeInstExtVO.getElectricCoreType());
                    ResponseDTO<ElectricConfigDTO> responseDTO = this.electricConfigService.getElectricConfigByCode(subTaskNodeInstExtVO.getElectricCoreType());
                    if (responseDTO.isSuccess()) {
                        exportDTO.setElectricCoreTypeName(responseDTO.getData().getType());
                    }
                    exportDTO.setElectricCoreTypeName(subTaskNodeInstExtVO.getElectricCoreTypeName());
                    exportDTO.setSortNo(subTaskNodeInstExtVO.getSortNo());
                    exportDTO.setTaskNodeName(subTaskNodeInstExtVO.getTaskNodeName());
                    exportDTO.setElectricCoreCode(subTaskNodeInstExtVO.getElectricCoreCode());
                    exportDTO.setCellType(subTaskNodeInstExtVO.getCellTypeCode());
                    ResponseDTO<ElectricConfigDTO> cellTypeResponse = this.cellTypeConfigService.getCellTypeConfigByCode(subTaskNodeInstExtVO.getCellTypeCode());
                    if (cellTypeResponse.isSuccess()) {
                        exportDTO.setCellTypeName(cellTypeResponse.getData().getType());
                    }
                    ResponseDTO<ElectricConfigDTO> factoryResponse = this.factoryConfigService.getFactoryConfigByCode(subTaskNodeInstExtVO.getFactoryCode());
                    if (factoryResponse.isSuccess()) {
                        exportDTO.setFactoryName(factoryResponse.getData().getType());
                    }

                    exportDTO.setDefectTypeNames(subTaskNodeInstExtVO.getDefectTypeNames());
                    exportMap.put("electricData", exportDTO);
                }
                if (Objects.equals(subTaskNodeInstExtVO.getSortNo(), TaskStepConstant.STEP_EIGHT)) {
                    //8 拆解结论：
                    SubTaskNodeInstExportDTO instExportDTO = new SubTaskNodeInstExportDTO();
                    instExportDTO.setSortNo(subTaskNodeInstExtVO.getSortNo());
                    instExportDTO.setTaskNodeName(subTaskNodeInstExtVO.getTaskNodeName());
                    if (CollectionUtils.isNotEmpty(subTaskNodeInstExtVO.getNodeInstProcessVOS())) {
                        List<String> configValues = subTaskNodeInstExtVO.getNodeInstProcessVOS().stream().map(NodeInstProcessVO::getConfigValue)
                                .collect(Collectors.toList());
                        instExportDTO.setRemark(StringUtils.join(configValues, ","));
                    }
                    exportMap.put("otherData", instExportDTO);
                }
                if (!Objects.equals(subTaskNodeInstExtVO.getSortNo(), TaskStepConstant.STEP_ONE) && !Objects.equals(subTaskNodeInstExtVO.getSortNo(), TaskStepConstant.STEP_EIGHT)) {
                    SubTaskNodeInstExportDTO dto = new SubTaskNodeInstExportDTO();
                    dto.setSortNo(subTaskNodeInstExtVO.getSortNo());
                    dto.setTaskNodeName(subTaskNodeInstExtVO.getTaskNodeName());
                    if (Objects.equals(subTaskNodeInstExtVO.getSortNo(), TaskStepConstant.STEP_SIX)) {
                        subTaskNodeInstExtVO.getNodeInstProcessVOS().forEach(e -> {
                            String name = e.getName().contains("极组条码") && StrUtil.isNotEmpty(e.getConfigValue()) ? e.getConfigValue() : e.getName();
                            if (OKEnum.OK.getValue().equals(e.getSelectValue())) {
                                name = name + "（" + e.getSelectValue() + "）";
                            } else {
                                String defectName = e.getDefectTypeList().stream()
                                        .map(DefectTypeVO::getDefectName)
                                        .collect(Collectors.joining(GlobalConst.Char.AIDescFileDefectTypeSplitChar));
                                name = name + "（" + defectName + "）";
                            }
                            e.setName(name);
                        });
                    } else if (Objects.equals(subTaskNodeInstExtVO.getSortNo(), TaskStepConstant.STEP_SEVEN)) {
                        List<String> configValues = subTaskNodeInstExtVO.getNodeInstProcessVOS().stream().map(NodeInstProcessVO::getConfigValue)
                                .collect(Collectors.toList());
                        dto.setRemark(StringUtils.join(configValues, ","));
                    } else if (!Objects.equals(subTaskNodeInstExtVO.getSortNo(), TaskStepConstant.STEP_THREE)) {
                        subTaskNodeInstExtVO.getNodeInstProcessVOS().forEach(e -> {
                            String name = e.getName();
                            if (StrUtil.isNotEmpty(e.getConfigValue())) {
                                name += "（" + e.getConfigValue();
                                if (StrUtil.isNotEmpty(e.getUnit()))
                                    name += StrUtil.SPACE + e.getUnit();
                                name += "）";
                            }
                            e.setName(name);
                        });
                    }
                    List<NodeInstProcessVO> nodeInstProcessVOS = subTaskNodeInstExtVO.getNodeInstProcessVOS();
                    // 多个元素
                    if (CollectionUtils.isNotEmpty(nodeInstProcessVOS)) {
                        List<NodeInstProcessExportDTO> nodeInstProcessExportDTOS = new ArrayList<>();
                        List<String> exceptionDescList = subTaskNodeInstExtVO.getNodeInstProcessVOS().stream()
                                .filter(p -> Objects.equals(SystemStatusEnum.YES.getCode(), p.getIsThrowDesc()))
                                .map(NodeInstProcessVO::getConfigValue)
                                .collect(Collectors.toList());
                        dto.setExceptionDesc(StringUtils.join(exceptionDescList, ","));
                        nodeInstProcessVOS.forEach(nodeInstProcessVO -> {
                            // 构造图片列表
                            if (CollectionUtil.isNotEmpty(nodeInstProcessVO.getTaskNodeFileList())) {
                                nodeInstProcessExportDTOS.addAll(this.buildNodeInstProcessPictureExportDTO(nodeInstProcessVO, subTaskNodeInstExtVO.getSortNo()));
                            } else {
                                nodeInstProcessExportDTOS.add(buildNodeInstProcessExportDTO(nodeInstProcessVO));
                            }
                        });
                        dto.setNodes(nodeInstProcessExportDTOS);
                    }
                    exportMap.put("nodeData" + subTaskNodeInstExtVO.getSortNo(), dto);
                }
            });
       }
        return exportMap;
    }


    private XWPFTemplate exportByTemplate(SubTaskAllVO subTaskAllVO) {
        Map<String, Object> exportMap = buildSubTaskAllData(subTaskAllVO);

        String templateFile = this.fileBasePath + FileModuleTypeEnum.EXPORT_TEMPLATE.getPath() + FileUtil.FILE_SEPARATOR + "Template.docx";
        RHLoopRowColumnTableRenderPolicy rowColumnTableRenderPolicy = new RHLoopRowColumnTableRenderPolicy(true);
        LoopColumnTableRenderPolicy loopColumnTableRenderPolicy = new LoopColumnTableRenderPolicy();
        Configure configure = Configure.builder()
                .bind("nodeData2.nodes", rowColumnTableRenderPolicy)
                .bind("nodeData3.nodes", loopColumnTableRenderPolicy)
                .bind("nodeData4.nodes", rowColumnTableRenderPolicy)
                .bind("nodeData5.nodes", rowColumnTableRenderPolicy)
                .bind("nodeData6.nodes", rowColumnTableRenderPolicy)
                .bind("nodeData7.nodes", rowColumnTableRenderPolicy)
                .build();
        XWPFTemplate template = XWPFTemplate.compile(templateFile, configure)
                .render(exportMap);
        return template;
    }

    private NodeInstProcessExportDTO buildNodeInstProcessExportDTO(NodeInstProcessVO nodeInstProcessVO) {
        if (nodeInstProcessVO == null) {
            return null;
        }
        NodeInstProcessExportDTO nodeInstProcessExportDTO = new NodeInstProcessExportDTO();
        nodeInstProcessExportDTO.setName(nodeInstProcessVO.getName());
        nodeInstProcessExportDTO.setConfigValue(nodeInstProcessVO.getConfigValue());
        nodeInstProcessExportDTO.setUnit(nodeInstProcessVO.getUnit());
        nodeInstProcessExportDTO.setSelectValue(nodeInstProcessVO.getSelectValue());
        nodeInstProcessExportDTO.setOtherValue(nodeInstProcessVO.getOtherValue());
        nodeInstProcessExportDTO.setDescr(nodeInstProcessVO.getDescr());
        nodeInstProcessExportDTO.setType(nodeInstProcessVO.getType());
        nodeInstProcessExportDTO.setIsThrowDesc(nodeInstProcessVO.getIsThrowDesc());
        nodeInstProcessExportDTO.setHasImage(nodeInstProcessVO.getHasImage());
        nodeInstProcessExportDTO.setFixed(nodeInstProcessVO.getFixed());
        nodeInstProcessExportDTO.setIsOther(nodeInstProcessVO.getIsOther());
        if (Objects.equals(nodeInstProcessVO.getHasImage(), SystemStatusEnum.YES.getCode())) {
            List<ImageEntity> images = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(nodeInstProcessVO.getTaskNodeFileList())) {
                nodeInstProcessVO.getTaskNodeFileList().forEach(fileVO -> {
                    images.add(buildImage(fileVO));
                    File imageFile = new File(this.fileBasePath + File.separator + fileVO.getFilePath());
                    if (imageFile.exists()) {
                        BufferedImage image = ImgUtil.read(imageFile);
                        nodeInstProcessExportDTO.setPicture(Pictures.ofBufferedImage(image, PictureType.suggestFileType(imageFile.getAbsolutePath())).size(206, 0).fitSize().create());
                    }
                });
            }
        }
        return nodeInstProcessExportDTO;
    }

    private List<NodeInstProcessExportDTO> buildNodeInstProcessPictureExportDTO(NodeInstProcessVO nodeInstProcessVO, Integer sortNo) {
        if (nodeInstProcessVO == null) {
            return new ArrayList<>();
        }
        List<NodeInstProcessExportDTO> exportDTOList = new ArrayList<>();
        int index = 1;
        List<TaskNodeFileVO> taskNodeFileVOList = nodeInstProcessVO.getTaskNodeFileList();
        for (TaskNodeFileVO taskNodeFileVO : taskNodeFileVOList) {
            NodeInstProcessExportDTO nodeInstProcessExportDTO = new NodeInstProcessExportDTO();
            nodeInstProcessExportDTO.setName(nodeInstProcessVO.getName() + StrUtil.DASHED + (index++));
            nodeInstProcessExportDTO.setConfigValue(nodeInstProcessVO.getConfigValue());
            nodeInstProcessExportDTO.setUnit(nodeInstProcessVO.getUnit());
            nodeInstProcessExportDTO.setSelectValue(nodeInstProcessVO.getSelectValue());
            nodeInstProcessExportDTO.setOtherValue(nodeInstProcessVO.getOtherValue());
            nodeInstProcessExportDTO.setDescr(nodeInstProcessVO.getDescr());
            nodeInstProcessExportDTO.setType(nodeInstProcessVO.getType());
            nodeInstProcessExportDTO.setIsThrowDesc(nodeInstProcessVO.getIsThrowDesc());
            nodeInstProcessExportDTO.setHasImage(nodeInstProcessVO.getHasImage());
            nodeInstProcessExportDTO.setFixed(nodeInstProcessVO.getFixed());
            nodeInstProcessExportDTO.setIsOther(nodeInstProcessVO.getIsOther());

            if (Objects.equals(nodeInstProcessVO.getHasImage(), SystemStatusEnum.YES.getCode()) &&
                    StrUtil.isNotEmpty(taskNodeFileVO.getFilePath())) {
                File imageFile = new File(this.fileBasePath + File.separator + taskNodeFileVO.getFilePath());
                if (imageFile.exists()) {
                    try {
                        BufferedImage image = ImgUtil.read(imageFile);
                        nodeInstProcessExportDTO.setPicture(Pictures.ofBufferedImage(image, PictureType.suggestFileType(imageFile.getAbsolutePath())).size(206, 0).fitSize().create());
                    } catch (Exception ex) {
                        log.error(ex.getMessage(), ex);
                    }
                }
            }

            exportDTOList.add(nodeInstProcessExportDTO);
        }

        return exportDTOList;
    }

    private ImageEntity buildImage(TaskNodeFileVO fileVO) {
        if (fileVO == null) {
            return null;
        }
        ImageEntity image = new ImageEntity();
        //image.setUrl(fileVO.getUrl());
        String imageFilePath = this.fileBasePath + File.separator + fileVO.getFilePath();
        image.setData(image2byte(imageFilePath));
        image.setType(ImageEntity.Data);
        return image;
    }

    public byte[] image2byte(String path) {
        byte[] data = null;
        FileImageInputStream input = null;
        try {
            input = new FileImageInputStream(new File(path));
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int numBytesRead = 0;
            while ((numBytesRead = input.read(buf)) != -1) {
                output.write(buf, 0, numBytesRead);
            }
            data = output.toByteArray();
            output.close();
            input.close();
        } catch (FileNotFoundException ex1) {
            ex1.printStackTrace();
        } catch (IOException ex1) {
            ex1.printStackTrace();
        }
        return data;
    }

}
